<!--
 * @author luguoxiang
 * @date 2022/9/17
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
-->
<template>
  <edit-container ref="containerRef" v-bind="containerConfig">
    <lg-form ref="formRef" v-loading="loading" :schema="schema" v-bind="formConfig">
      <template #component>
        <t-auto-complete
          v-model="data.component"
          style="width: 100%"
          :options="options"
          highlight-keyword
          placeholder="请输入页面组件"
          clearable
          @change="componentChange"
        />
      </template>
      <template #path>
        <t-auto-complete
          v-model="data.path"
          style="width: 100%"
          :options="pathOptions"
          highlight-keyword
          placeholder="请输入路径"
          clearable
        />
      </template>
      <template v-if="data.type === 1" #append>
        <t-space direction="vertical" style="'--lg-grid-min: 2;--lg-grid-max: 2'">
          <t-space>
            <t-button @click="createPermits">生成权限列表</t-button>
            <t-button v-if="permits.length" @click="addPermit">添加</t-button>
            <t-button v-if="permits.length" theme="danger" @click="clear">清空</t-button>
            <t-button v-if="permits.length" theme="default" @click="autovisible = true">自动匹配接口权限</t-button>
          </t-space>
          <template v-if="permits.length">
            <t-table ref="tableRef" row-key="code" :columns="columns" :data="permits" bordered>
              <template #actionbar="{ rowIndex }">
                <t-link theme="danger" @click="removePermit(rowIndex)">删除</t-link>
              </template>
            </t-table>
          </template>
        </t-space>
      </template>
    </lg-form>
    <t-dialog v-model:visible="autovisible" @confirm="autoMatch">
      <t-input v-model="autoCode" placeholder="请输入接口权限前缀" />
    </t-dialog>
  </edit-container>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { Input, MessagePlugin, PrimaryTableCol, Select } from 'tdesign-vue-next';
import { EditContainer, useEditContainer } from '@/components';
import { LgForm, syncLockLoad } from '@/soar';
import schema from './schema';
import api from './api';
import { COMPONENTS } from '@/constants';
import permitApi from '@/pages/system/auth/permit/api';

const emits = defineEmits(['ok']);

const components = [];
const containerRef = ref();
const formRef = ref();
const options = ref([]);
const permits = ref<{ title: string; code: string; permitIds: string[] }[]>([]);

const columns = ref<PrimaryTableCol[]>([]);
let apiList = null;
function getApiList() {
  if (apiList) {
    return Promise.resolve(apiList);
  }
  return syncLockLoad('load:permit', () =>
    permitApi.query({ $soarQuery: { fields: 'id,name,code' }, queryType: 'list', pageSize: 99999 }),
  ).then((res: any) => {
    apiList = res.list.map((x: any) => ({
      value: x.id,
      label: `【${x.name}】${x.code}`,
      codes: x.code.split('/').reverse(),
    }));
    return apiList;
  });
}
getApiList().then((res: any[]) => {
  columns.value = [
    {
      title: '名称',
      colKey: 'title',
      width: 150,
      edit: {
        keepEditMode: true,
        defaultEditable: true,
        component: Input,
        onEdited: (context: any) => {
          permits.value.splice(context.rowIndex, 1, context.newRowData);
        },
        rules: [
          { required: true, message: '必填' },
          { validator: (val) => permits.value.filter((x: any) => x.title === val).length < 2, message: '名称重复' },
        ],
      },
    },
    {
      title: '编码',
      colKey: 'code',
      width: 300,
      edit: {
        keepEditMode: true,
        defaultEditable: true,
        component: Input,
        onEdited: (context) => {
          permits.value.splice(context.rowIndex, 1, context.newRowData);
        },
        rules: [
          { required: true, message: '必填' },
          { validator: (val) => permits.value.filter((x: any) => x.code === val).length < 2, message: '编码重复' },
        ],
      },
    },
    {
      title: '接口权限',
      colKey: 'permitIds',
      edit: {
        keepEditMode: true,
        defaultEditable: true,
        component: Select,
        props: {
          options: res,
          multiple: true,
          filterable: true,
        },
        onEdited: (context: any) => {
          permits.value.splice(context.rowIndex, 1, context.newRowData);
        },
      },
    },
    {
      title: '操作',
      colKey: 'actionbar',
      width: 60,
    },
  ];
});

function getPermitIds(code: string) {
  const cs = code
    .split(/[_:-\\/]/)
    .reverse()
    .filter((x: string) => x);
  const filter = (list: any[], index: number, str: string) => {
    const rs = list.filter((x) => x.codes[index] === str);
    if (rs.length === 0) {
      return list;
    }
    if (rs.length === 1) {
      return rs;
    }
    index += 1;
    if (index >= cs.length) {
      return rs;
    }
    return filter(rs, index, cs[index]);
  };
  return getApiList().then((res: any) => {
    const ls = filter(res, 0, cs[0]);
    if (ls.length === 1) {
      return ls.map((y: any) => y.value);
    }
    return [];
  });
}

const autovisible = ref(false);
const autoCode = ref('');

/**
 * 自动匹配
 */
async function autoMatch() {
  for (const x of permits.value) {
    if (x.permitIds?.length) {
      continue;
    }
    // eslint-disable-next-line no-await-in-loop
    x.permitIds = await getPermitIds(x.code);
    if (x.permitIds?.length) {
      continue;
    }
    const cs = x.code.split('/');
    // eslint-disable-next-line no-await-in-loop
    x.permitIds = await getPermitIds(`${autoCode.value}/${cs[cs.length - 1]}`);
  }
  permits.value = [...permits.value];
  autovisible.value = false;
}

/**
 * 生成权限列表
 */
async function createPermits() {
  const base = data.value.code || data.value.path;
  if (base) {
    const ps = [
      { title: '详情', code: `${base}/detail`, permitIds: await getPermitIds(`${base}/detail`) },
      { title: '新增', code: `${base}/create`, permitIds: await getPermitIds(`${base}/create`) },
      { title: '更新', code: `${base}/update`, permitIds: await getPermitIds(`${base}/update`) },
      { title: '删除', code: `${base}/remove`, permitIds: await getPermitIds(`${base}/remove`) },
      { title: '导入', code: `${base}/import`, permitIds: await getPermitIds(`${base}/import`) },
      { title: '导出', code: `${base}/export`, permitIds: await getPermitIds(`${base}/export`) },
    ];
    for (const p of ps) {
      if (permits.value.findIndex((x: any) => x.title === p.title) === -1) {
        permits.value.push(p);
      }
    }
  } else {
    MessagePlugin.error('请输入权限码或路径');
  }
}

/**
 * 添加权限
 */
function addPermit() {
  const base = data.value.code || data.value.path;
  permits.value.push({ title: '', code: `${base}/`, permitIds: [] });
}

/**
 * 清空权限列表
 */
function clear() {
  permits.value = [];
}

/**
 * 删除权限
 * @param index
 */
function removePermit(index: number) {
  permits.value.splice(index, 1);
}

const { containerConfig, formConfig, loading, open, data } = useEditContainer<any>({
  header: '菜单',
  containerRef,
  formRef,
  getData: (params: any) =>
    api.detail(params.id).then((res: any) => {
      permits.value = res.permits || [];
      return res;
    }),
  onOk: async ({ values, data, params, isUpdate, changed }) => {
    values.id = data.id;
    if (!values.code) {
      values.code = values.path;
    }
    values.permits = permits.value;
    await api.saveOrUpdate(values, isUpdate);
    emits('ok', values);
  },
  containerProps: {
    width: '1200px',
  },
  onOpen(params: any) {
    permits.value = [];
    options.value = Object.keys(COMPONENTS);
  },
  data: { type: 1, status: 0, parentId: '0' },
});

const pathOptions = ref([]);
function componentChange(e: any) {
  pathOptions.value = [];
  if (e?.startsWith?.('pages/')) {
    const path = e.replace(/^pages\//, '').replace(/\.vue$/, '');
    const paths = path.split('/');
    for (let i = paths.length; i > 1; i--) {
      pathOptions.value.push(`/${paths.slice(0, i).join('/')}`);
    }
    for (let i = 1, len = paths.length - 1; i < len; i++) {
      pathOptions.value.push(`/${paths.slice(i).join('/')}`);
    }
    for (const x of paths) {
      pathOptions.value.push(`/${x}`);
    }
  }
}

defineExpose({ open });
</script>
